#pragma once
#include <cassert>
#include <mutex>
#include "util.hpp"

class user_table
{
public:
    user_table(const std::string &host, const std::string &username,
        const std::string &password, const std::string &dbname, uint16_t port = 3306) 
    {
        _mysql = mysql_util::mysql_create(host, username, password, dbname, port);
        assert(_mysql != nullptr);
    }
    ~user_table() 
    {
        mysql_util::mysql_destory(_mysql);
        _mysql = nullptr;
    }
    // 注册时新增用户：用户名和密码用Json格式存储
    bool insert(Json::Value& user)
    {
        if (user["username"].isNull() || user["password"].isNull()) {
            DLOG("input user or passward");
            return false;
        }
        #define INSERT_USER "insert user values(null, '%s', '%s', 1000, 0, 0);"
        char sql[4096] = {0};
        sprintf(sql, INSERT_USER, user["username"].asCString(), user["password"].asCString());
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false) {
            DLOG("insert user info failed");
            return false;
        }
        return true;
    }
    // 登入验证，并返回详细的用户信息
    bool login(Json::Value& user)
    {
        if (user["username"].isNull() || user["password"].isNull()) {
            DLOG("input user or passward");
            return false;
        }
        // 以用户名和密码共同作为查询的过滤条件，查询到数据则表示用户名密码一致，没有信息则用户名密码错误
        #define LOGIN_USER "select id, score, total_count, win_count from user where username='%s' and password='%s';"
        char sql[4096] = {0};
        sprintf(sql, LOGIN_USER, user["username"].asCString(), user["password"].asCString());
        MYSQL_RES* res = nullptr;
        {
            // 注意：访问数据和保存数据到本地必须是原子的，否则多线程中存在线程安全问题
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false) {
                DLOG("user login failed");
                return false;
            }
            // 要么查询不到数据，要么数据只存在一条
            res = mysql_store_result(_mysql);
            if (res == nullptr) {
                DLOG("have no user login info");
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if (row_num != 1) {
            DLOG("user info is not unique");
            return false;
        }
        // 额外添加用户游戏分数、总场数、胜场数信息
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = std::stol(row[0]);
        user["score"] = std::stol(row[1]);
        user["total_count"] = std::stol(row[2]);
        user["win_count"] = std::stol(row[3]);
        mysql_free_result(res);
        return true;
    }
    // 通过用户名获取用户信息
    bool select_by_name(const std::string& name, Json::Value& user)
    {
        #define USER_BY_NAME "select id, score, total_count, win_count from user where username='%s';"
        char sql[4096] = {0};
        sprintf(sql, USER_BY_NAME, name.c_str());
        MYSQL_RES* res = nullptr;
        {
            // 注意：访问数据和保存数据到本地必须是原子的，否则多线程中存在线程安全问题
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false) {
                DLOG("get user by name failed");
                return false;
            }
            // 要么查询不到数据，要么数据只存在一条
            res = mysql_store_result(_mysql);
            if (res == nullptr) {
                DLOG("have no user login info");
                return false;
            }
        }
        int row_num = mysql_num_rows(res);
        if (row_num != 1) {
            DLOG("user info is not unique");
            return false;
        }
        // 额外添加用户游戏分数、总场数、胜场数信息
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = std::stol(row[0]);
        user["username"] = name;
        user["score"] = std::stol(row[1]);
        user["total_count"] = std::stol(row[2]);
        user["win_count"] = std::stol(row[3]);
        mysql_free_result(res);
        return true;
    }
    // 通过id获取用户信息
    bool select_by_id(uint64_t id, Json::Value& user)
    {
        #define USER_BY_ID "select username, score, total_count, win_count from user where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, USER_BY_ID, id);
        MYSQL_RES* res = nullptr;
        {
            // 注意：访问数据和保存数据到本地必须是原子的，否则多线程中存在线程安全问题
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret = mysql_util::mysql_exec(_mysql, sql);
            if (ret == false) {
                DLOG("get user by id failed");
                return false;
            }
            // 要么查询不到数据
            res = mysql_store_result(_mysql);
            if (res == nullptr) {
                DLOG("have no user login info");
                return false;
            }
        }
        // 要么数据只存在一条
        int row_num = mysql_num_rows(res);
        if (row_num != 1) {
            DLOG("user info is not unique");
            return false;
        }
        // 额外添加用户游戏分数、总场数、胜场数信息
        MYSQL_ROW row = mysql_fetch_row(res);
        user["id"] = id;
        user["username"] = row[0];
        user["score"] = std::stol(row[1]);
        user["total_count"] = std::stol(row[2]);
        user["win_count"] = std::stol(row[3]);
        mysql_free_result(res);
        return true;
    }
    // 胜利时天梯分数增加30分，战斗场数增加1，胜利场数增加1
    bool win(uint64_t id)
    {
        #define USER_WIN "update user set score=score+30, total_count=total_count+1, win_count=win_count+1 where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, USER_WIN, id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false) {
            DLOG("update win user info failed");
            return false;
        }
        return true;
    }
    // 失败时天梯分数减少30分，战斗场数增加1
    bool lose(uint64_t id)
    {
        #define USER_LOSE "update user set score=score-30, total_count=total_count+1 where id=%ld;"
        char sql[4096] = {0};
        sprintf(sql, USER_LOSE, id);
        bool ret = mysql_util::mysql_exec(_mysql, sql);
        if (ret == false) {
            DLOG("update lose user info failed");
            return false;
        }
        return true;
    }

private:
    MYSQL* _mysql; // mysql操作句柄
    std::mutex _mutex; // 互斥锁保护数据库访问操作
};